package org.jef.security.service;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.jef.core.constant.CacheConstants;
import org.jef.core.constant.CommonConstants;
import org.jef.core.constant.SecurityConstants;
import org.jef.core.mask.CommonMaskStrategy;
import org.jef.core.util.R;
import org.jef.core.util.RetOps;
import org.jef.upms.api.dto.UserDTO;
import org.jef.upms.api.dto.UserInfo;
import org.jef.upms.api.entity.SysRole;
import org.jef.upms.api.entity.SysUser;
import org.jef.upms.api.feign.RemoteParamService;
import org.jef.upms.api.feign.RemoteUserService;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.*;

/**
 * @version 1.0
 * @program: PigWechatUserDetailsServerImpl
 * @description: 微信小程序用户详细信息
 * @author: ChengZhi 0336
 * @create: 2024-08-05 19:18
 **/
@Slf4j
@RequiredArgsConstructor
public class JefWechatUserDetailsServerImpl implements PigUserDetailsService{

	private final RemoteUserService remoteUserService;

	private final CacheManager cacheManager;

	private final WxMaService wxMaService;

	private final RemoteParamService remoteParamService;

	/**
	 * @param code
	 * @return
	 * @throws UsernameNotFoundException
	 */
	@Override
	public UserDetails loadUserByUsername(String code) throws UsernameNotFoundException {

		String[] s = StringUtils.split(code, "_");
		String openId = "";
		String jsCode = s[0];
		String encryptedData = s[1];
		String iv = s[2];

		if ("null".equalsIgnoreCase(encryptedData) || "null".equalsIgnoreCase(iv)) {
			try {
				WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(jsCode);
				log.info(session.getSessionKey());
				log.info(session.getOpenid());
				openId = session.getOpenid();
				UserDetails user = getUserByOpenId(openId);
				if (user != null) {
					return user;
				} else {
					return getDefalutUserDetails();
				}
			} catch (WxErrorException e) {
				log.error("调用微信异常", e);
				throw new IllegalArgumentException("调用微信异常");
			} finally {
				WxMaConfigHolder.remove();//清理ThreadLocal
			}

		}

		if (StringUtils.isBlank(jsCode)) {
			throw new IllegalArgumentException("请输入正确的code，请核实！");
		}
		String appid = remoteParamService.getByKey("WECHAT_APPID").getData();
		if (!wxMaService.switchover(appid)) {
			throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置，请核实！", appid));
		}

		try {
			WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(jsCode);
			log.info(session.getSessionKey());
			log.info(session.getOpenid());
			openId = session.getOpenid();
			String sessionKey = session.getSessionKey();
			WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
			String phoneId = phoneNoInfo.getPhoneNumber();
			log.info("当前登录用户手机号为：{}", phoneId);

			UserDTO userDTO = new UserDTO();
			userDTO.setWxOpenid(openId);
			userDTO.setPhone(phoneId);
			R<UserInfo> result = remoteUserService.queryAndRegiste(userDTO);
			UserDetails userDetails = getUserDetails(result);
			return userDetails;
		} catch (WxErrorException e) {
			log.error("调用微信异常", e);
			throw new IllegalArgumentException("调用微信异常");
		} finally {
			WxMaConfigHolder.remove();//清理ThreadLocal
		}

	}

	/**
	 * 创建默认用户
	 * @return
	 */
	private UserDetails getDefalutUserDetails() {

		UserDTO userDTO = new UserDTO();
		userDTO.setWxOpenid("wx_public_anonymous_open_id");
		R<UserInfo> result = remoteUserService.queryAndRegisteAnonymousUser(userDTO);

		UserInfo info = RetOps.of(result).getData().orElseThrow(() -> new UsernameNotFoundException("用户不存在"));

		Set<String> dbAuthsSet = new HashSet<>();

		if (ArrayUtil.isNotEmpty(info.getRoles())) {
			// 获取角色
			Arrays.stream(info.getRoles()).forEach(role -> dbAuthsSet.add(SecurityConstants.ROLE + role));
			// 获取资源
			dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));
		}

		Collection<GrantedAuthority> authorities = AuthorityUtils
				.createAuthorityList(dbAuthsSet.toArray(new String[0]));
		SysUser user = info.getSysUser();

		// 前台抹敏感展示
		String phone = user.getPhone();
		CommonMaskStrategy maskStategy = new CommonMaskStrategy("*(3,7)");
		phone = maskStategy.eval(phone, null);
		// 构造security用户
		return new PigUser(user.getUserId(), user.getDeptId(),user.getName(), user.getUsername(),
				SecurityConstants.BCRYPT + user.getPassword(), phone, user.getWxOpenid() ,true, true, true,
				StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL), authorities);
	}

	public UserDetails getUserByOpenId(String openId) {
		Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
		if (cache != null && cache.get(openId) != null) {
			return (PigUser) cache.get(openId).get();
		}
		UserDTO userDTO = new UserDTO();
		userDTO.setWxOpenid(openId);
		R<UserInfo> result = remoteUserService.queryByXwOpenId(userDTO);
		if (result == null) {
			return null;
		}
		UserDetails userDetails = getUserDetails(result);
		if (cache != null) {
			cache.put(openId, userDetails);
		}
		return userDetails;
	}
	/**
	 * check-token 使用
	 * @param pigUser user
	 * @return
	 */
	@Override
	public UserDetails loadUserByUser(PigUser pigUser) {
		return this.getUserByOpenId(pigUser.getOpenId());
	}

	/**
	 * 是否支持此客户端校验
	 * @param clientId 目标客户端
	 * @return true/false
	 */
	@Override
	public boolean support(String clientId, String grantType) {
		return SecurityConstants.WECHAT.equals(grantType);
	}
}
